package com.ysy.tz.core.selfcomp;

import com.ysy.tz.core.engine.Modelable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.control.Skin;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.stage.Popup;
import org.apache.commons.collections4.CollectionUtils;

public class PopupTextField extends TextField {
    private Popup popup;
    private CusomterTableView tableView;
    private TextChangeListener textChangeListener;
    private ChangeListener textPropertyChangeListner;
    private EventHandler enterHandler;
    private TableColumn tableColumns = null;
    private DoubleProperty popPrefWidth = new SimpleDoubleProperty();
    private DoubleProperty popPrefHeight = new SimpleDoubleProperty();
    private ObjectProperty<Modelable> model = new SimpleObjectProperty();
    private BooleanProperty textChangeHit = new SimpleBooleanProperty(true);
    private BooleanProperty enterHit = new SimpleBooleanProperty(true);

    public PopupTextField() {
        this.initPopup();
    }

    public PopupTextField(String text) {
        super(text);
        this.initPopup();
    }

    private void initPopup() {
        this.popup = new Popup();
        this.tableView = new CusomterTableView();
        this.popup.getContent().add(this.tableView);
        this.popPrefWidth.addListener((observable, oldValue, newValue) -> {
            this.tableView.setPrefWidth(this.popPrefWidth.get());
        });
        this.popPrefHeight.addListener((observable, oldValue, newValue) -> {
            this.tableView.setPrefHeight(this.popPrefHeight.get());
        });
        this.tableView.addEventHandler(KeyEvent.KEY_PRESSED, (keyEvent) -> {
            if (keyEvent.getCode() == KeyCode.ESCAPE) {
                this.popup.hide();
            } else if (keyEvent.getCode() == KeyCode.ENTER) {
                Object obj = this.tableView.getSelectionModel().getSelectedItem();
                if (obj != null && this.textChangeListener != null) {
                    if (this.textChangeHit.get()) {
                        this.textProperty().removeListener(this.textPropertyChangeListner);
                    }

                    this.textChangeListener.PopupChoise(obj);
                    if (this.textChangeHit.get()) {
                        this.textProperty().addListener(this.textPropertyChangeListner);
                    }

                    this.positionCaret(this.getText().length());
                    this.popup.hide();
                }
            }

        });
        this.tableView.addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> {
            if (event.getClickCount() == 2 && this.tableView.getSelectionModel().getSelectedIndex() >= 0) {
                Object obj = this.tableView.getSelectionModel().getSelectedItem();
                if (obj != null && this.textChangeListener != null) {
                    if (this.textChangeHit.get()) {
                        this.textProperty().removeListener(this.textPropertyChangeListner);
                    }

                    this.textChangeListener.PopupChoise(obj);
                    if (this.textChangeHit.get()) {
                        this.textProperty().addListener(this.textPropertyChangeListner);
                    }

                    this.positionCaret(this.getText().length());
                    this.popup.hide();
                }
            }

        });
        this.focusedProperty().addListener((observable, oldValue, newValue) -> {
            if (!newValue && this.popup.isShowing()) {
                this.popup.hide();
            }

        });
        this.textPropertyChangeListner = new ChangeListener() {
            public void changed(ObservableValue observable, Object oldValue, Object newValue) {
                String enteredText = PopupTextField.this.getText();
                if (enteredText != null && !enteredText.isEmpty()) {
                    PopupTextField.this.fireTextChange(enteredText);
                } else {
                    PopupTextField.this.popup.hide();
                }

            }
        };
        this.enterHandler = new EventHandler<KeyEvent>() {
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.ENTER) {
                    PopupTextField.this.fireTextChange(PopupTextField.this.getText());
                    event.consume();
                }

            }
        };
        this.textProperty().addListener(this.textPropertyChangeListner);
        this.addEventHandler(KeyEvent.KEY_PRESSED, this.enterHandler);
        this.textChangeHit.addListener((observable, oldValue, newValue) -> {
            this.textProperty().removeListener(this.textPropertyChangeListner);
            if (newValue) {
                this.textProperty().addListener(this.textPropertyChangeListner);
            }

        });
        this.enterHit.addListener((observable, oldValue, newValue) -> {
            this.removeEventHandler(KeyEvent.KEY_PRESSED, this.enterHandler);
            if (newValue) {
                this.addEventHandler(KeyEvent.KEY_PRESSED, this.enterHandler);
            }

        });
    }

    protected Skin<?> createDefaultSkin() {
        this.getScene().getWindow().xProperty().addListener((observable, oldValue, newValue) -> {
            if (this.popup.isShowing()) {
                this.popup.hide();
            }

        });
        this.getScene().getWindow().yProperty().addListener((observable, oldValue, newValue) -> {
            if (this.popup.isShowing()) {
                this.popup.hide();
            }

        });
        return super.createDefaultSkin();
    }

    private void fireTextChange(String enteredText) {
        if (this.textChangeListener != null) {
            ObservableList list = this.textChangeListener.textChanged(this, enteredText);
            if (CollectionUtils.isNotEmpty(list)) {
                this.tableView.setItems(list);
                Bounds bounds = this.localToScreen(this.getBoundsInLocal());
                this.popup.show(this, bounds.getMinX(), bounds.getMinY() + this.getHeight() + 2.0);
                this.tableView.getSelectionModel().select(0);
                this.tableView.requestFocus();
            }
        }

    }

    public void addTextChangeListener(TextChangeListener textChangeListener) {
        this.textChangeListener = textChangeListener;
    }

    public void addPopChoiseChangeListener(TextChangeListener textChangeListener) {
        this.textChangeListener = textChangeListener;
    }

    public TableColumn getTableColumns() {
        return this.tableColumns;
    }

    public void setTableColumns(TableColumn tableColumns) {
        this.tableView.getColumns().add(tableColumns);
    }

    public double getPopPrefWidth() {
        return this.popPrefWidth.get();
    }

    public DoubleProperty popPrefWidthProperty() {
        return this.popPrefWidth;
    }

    public void setPopPrefWidth(double popPrefWidth) {
        this.popPrefWidth.set(popPrefWidth);
    }

    public double getPopPrefHeight() {
        return this.popPrefHeight.get();
    }

    public DoubleProperty popPrefHeightProperty() {
        return this.popPrefHeight;
    }

    public void setPopPrefHeight(double popPrefHeight) {
        this.popPrefHeight.set(popPrefHeight);
    }

    public boolean isTextChangeHit() {
        return this.textChangeHit.get();
    }

    public BooleanProperty textChangeHitProperty() {
        return this.textChangeHit;
    }

    public void setTextChangeHit(boolean textChangeHit) {
        this.textChangeHit.set(textChangeHit);
    }

    public boolean isEnterHit() {
        return this.enterHit.get();
    }

    public BooleanProperty enterHitProperty() {
        return this.enterHit;
    }

    public void setEnterHit(boolean enterHit) {
        this.enterHit.set(enterHit);
    }

    public Modelable getModel() {
        return (Modelable)this.model.get();
    }

    public ObjectProperty<Modelable> modelProperty() {
        return this.model;
    }

    public void setModel(Modelable model) {
        this.setText(model == null ? "" : model.getModelStringValue());
        this.model.set(model);
    }
}
